using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using Needle.Utils.Extensions;
using Needle.Framework;

namespace Needle.Simulation
{
    public abstract class Process : INotifyDisposed
    {

        #region Events

        internal event Action<int, Task> TaskScheduled;
        internal event Action<int, Process> ProcessSpawned;
        internal event Action<int, string> MessageLogged;

        #endregion

        #region fields and properties...

        private IEnumerator<Task> _processStep;
        public int Id { get; private set; }
        public string Name { get; set; }
        public string LastEvent { get; set; }

        #endregion

        #region Process id generator

        private static int _nextProcessId = 0;
        public static int GetNextId()
        {
            return _nextProcessId++;
        }

        #endregion

        #region constructors...

        public Process()
        {
            _processStep = ProcessDefinition().GetEnumerator();
            Id = GetNextId();
        }

        #endregion

        #region Process management and logging

        /// <summary>
        /// schedules the next process step for execution.
        /// </summary>
        /// <returns>True if a process step was scheduled, false if it was not</returns>
        public bool Schedule()
        {
            if (!IsDisposed && _processStep.MoveNext())
            {
                ScheduleTask(_processStep.Current);
                return true;
            }
            return false;
        }
        /// <summary>
        /// alias for Schedule
        /// </summary>
        public bool Resume()
        {
            return Schedule();
        }

        protected void ScheduleTask(Task task)
        {
            if (task == null || TaskScheduled == null)
                return;

            TaskScheduled(this.Id, task);
        }

        protected void Spawn(Process process)
        {
            if (process == null || ProcessSpawned == null)
                return;

            ProcessSpawned(this.Id, process);
        }

        protected void Log(string message)
        {
            if (String.IsNullOrEmpty(message) || MessageLogged == null)
                return;

            MessageLogged(this.Id, message);
        }

        protected void Log(string message, params object[] args)
        {
            if (String.IsNullOrEmpty(message) || MessageLogged == null)
                return;

            MessageLogged(this.Id, message.FormattedWith(args));
        }

        #endregion

        #region Tasks

        protected Task Delay(TimeSpan delay)
        {
            return Task.Delay(this.Id, delay, () =>
            {
                if (IsDisposed)
                    return;
                Resume();
            });
        }

        protected Task Delay(TimeSpan delay, Action action)
        {
            return Task.Delay(this.Id, delay, () =>
            {
                if (IsDisposed)
                    return;
                if (action != null)
                    action();
                Resume();
            });
        }

        protected Task WaitEvent(string eventName)
        {
            return Task.WaitEvent(this.Id, eventName, () =>
            {
                if (IsDisposed)
                    return;
                LastEvent = eventName;
                Resume();
            });
        }

        protected Task WaitEvent(string eventName, Action action)
        {
            return Task.WaitEvent(this.Id, eventName, () =>
            {
                if (IsDisposed)
                    return;
                LastEvent = eventName;
                if (action != null)
                    action();
                Resume();
            });
        }

        protected Task RaiseEvent(string eventName)
        {
            return Task.RaiseEvent(this.Id, eventName, () =>
            {
                if (IsDisposed)
                    return;
                Resume();
            });
        }

        protected Task Suspend()
        {
            return Task.Suspend(this.Id, null);
        }

        #endregion

        #region ProcessDefinition 

        protected abstract IEnumerable<Task> ProcessDefinition();

        #endregion

        #region IDisposeNotification

        public event EventHandler Disposed;

        public bool IsDisposed { get; private set; }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        ~Process()
        {
            Dispose(false);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                EventHandler handler = Disposed;
                if (handler != null)
                    handler(this, EventArgs.Empty);
            }
            IsDisposed = true;
        }

        #endregion

    }

}
